#include "LoopSubdivider.h"

//==============================================================================
// Public Interface
//==============================================================================

/**
 *	Completes one subdivision iteration, setting the connectivity and 
 *  subdivision meshes within the ModelFrame based on its coarse mesh
 */
void LoopSubdivider::subdivide( ModelFrame* frame )
{
	// call the super function first
	Subdivider::subdivide(frame);

	// only subdivide if there is a coarse mesh loaded
	if( frame->coarse && frame->coarse->n_vertices() > 0)
	{
		prepare( &frame->coarse, &frame->reconnected, &frame->subdivided );

		mesh->triangulate();
		createMeshes( );
		checkImportance( frame );
		
		cleanup( &frame->coarse, &frame->reconnected, &frame->subdivided );
	}
}


Point LoopSubdivider::doSubdivisionMask ( PolyMesh* m, VHandle& vh )
{
	VVIter vvIt;
	Point pNew;
	Point eSum(0,0,0);
	int n( mesh->valence( vh ) );

	//calculate beta
	double inner = pow(((3.0/8.0) + 0.25*(cos((2*PI)/n))),2);
	double beta = (1.0/n) * ((5.0/8.0) - inner);

	// find the sum of adjacent edge points
	for( vvIt = m->vv_iter( vh ); vvIt != false; ++vvIt )
		eSum += m->point( vvIt.handle() );

	// weighted formula for vertex_vertex point
	pNew = ( 1.0 - n*beta )*m->point(vh) + beta*eSum;

	return pNew;

}
/**
 *	
 */
void LoopSubdivider::showConnectivity( const Selection* s )
{
	ModelFrame* frame = s->selectionSource;
	if( frame && s && s->type == FACE )
		calcConnectivity( s );
	else if( s && s->type == EDGE )
	{
		EHandle eh = s->selectedEdge;
		//make sure selected edge is valid in the course mesh
		if( !(frame && frame->coarse && frame->coarse->is_valid_handle( s->selectedEdge )) )
			return;		

		doSharpEdgeSetting( frame, eh );
	}
}

/**
 *	This function calculates the importance mesh based on a selected face.
 *  Basically, the importance mesh is the portion of the reconnected mesh that
 *  lies within the selected face.
 */
void LoopSubdivider::calcConnectivity( const Selection* s )
{
	ModelFrame* frame = s->selectionSource;
	FHandle tempFH(s->selectedFace);
	PolyMesh *tempImp = new PolyMesh();

	if(!s)
		return;

	//check if meshes exist
	if(!( frame && frame->coarse && frame->reconnected && frame->subdivided ))
		return;

	if( !frame->coarse->is_valid_handle( tempFH ) )
		return;

	if( frame->getImportanceMesh() )
		delete frame->getImportanceMesh();	

	//check to see if the handle selected is part of the coarse mesh
	if( !frame->coarse->is_valid_handle( s->selectedFace ) )
		return;
	
	loadImportance( frame, tempFH, tempImp );

	tempImp->request_face_normals();
	tempImp->request_vertex_normals();
	tempImp->update_normals();
	frame->setImportanceMesh(tempImp);
}

void LoopSubdivider::loadImportance( ModelFrame* frame, FHandle tempFH, PolyMesh* tempImp )
{
	std::vector< int > indices = frame->coarse->data(tempFH).get_indices();
	std::vector<VHandle> theVerts;
	FHandle fHandle;
	VData vd;

	//since we know each coarse face index corresponds to the first of the 4 "split" face indexes...
	for( int i = 0; i < indices.size(); i++ )
	{		
		theVerts.clear();
	
		fHandle = frame->reconnected->face_handle(indices[i]); //grab the current face handle	

		for(PolyMesh::FaceVertexIter fv_i(*(frame->reconnected),fHandle); fv_i != false; ++fv_i)
		{
			Vec4f color = frame->reconnected->color( fv_i.handle() );
			VHandle vertex = tempImp->add_vertex(frame->reconnected->point(fv_i.handle()));
			vd = tempImp->data( vertex );
			vd.set_importance( fv_i.handle().idx() );
			vd.set_weight( frame->reconnected->data( fv_i.handle() ).weight() );
			tempImp->data( vertex ) = vd;
			tempImp->set_color( vertex, color );
			theVerts.push_back(vertex); //Store the verts of this triangle
		}
			
		FHandle face = tempImp->add_face(theVerts);

		//save edge color
		FEIter feit( frame->reconnected->fe_iter( fHandle ) );
		FEIter feit2( tempImp->fe_iter( face ) );
		for(; feit != false; ++feit )
		{
			EData ed = frame->reconnected->data( feit.handle() );
			tempImp->data( feit2.handle() ) = ed;
			++feit2;
		}

		FData fd = tempImp->data( face );
		fd.set_importance( fHandle.idx() );
		tempImp->data( face ) = fd;
	}
}


/**
 *	This loads the given "im" mesh with the importance mesh for the specified vertex handle and
 *  frame.
 */
void LoopSubdivider::loadImportance( ModelFrame* frame, VHandle& vh, PolyMesh* im )
{
	FData fd;
	VData vd, vdSub;
	FHandle fC;
	FData fdC;

	vd = frame->reconnected->data( vh );
	vd.setColor( frame->reconnected->color( vh ) );
	frame->reconnected->set_color( vh, Vec4f(  0.75, 0, 0.2, 1 ));

	if( vd.getType() == EDGE_VERTEX )
	{
		// get the edges on the left and right of this vertex and the original edge
		// from coarse mesh
		EHandle left, right, coarse;
		coarse = frame->coarse->edge_handle( vd.importance() );
		
		// now get the vertices in coarse mesh
		HHandle heh,opp;
		VHandle vh1, vh2, vh3, vh4;
		heh = frame->coarse->halfedge_handle( coarse, 0 );
		opp = frame->coarse->halfedge_handle( coarse, 1 );
		vh1 = frame->coarse->from_vertex_handle( heh );
		vh2 = frame->coarse->to_vertex_handle( heh );

		//get the faces in the coarse mesh
		FHandle fh1 = frame->coarse->face_handle( heh );
		FHandle fh2 = frame->coarse->opposite_face_handle( heh );

		// which will help us get the vertices in the connected mesh
		vh1 = frame->reconnected->vertex_handle( frame->coarse->data( vh1 ).get_index() );
		vh2 = frame->reconnected->vertex_handle( frame->coarse->data( vh2 ).get_index() );

		//now get the vertices opposite this edge vertex
		VHandle vCoarse1, vCoarse2;
		vCoarse1 = frame->coarse->to_vertex_handle( frame->coarse->next_halfedge_handle( heh ) );
		vCoarse2 = frame->coarse->to_vertex_handle( frame->coarse->next_halfedge_handle( opp ) );
		vh3 = frame->reconnected->vertex_handle( frame->coarse->data( vCoarse1 ).get_index() );
		vh4 = frame->reconnected->vertex_handle( frame->coarse->data( vCoarse2 ).get_index() );

		//now save the weights in the face vertices and vertex vertices
		VData vd1, vd2, vd3, vd4;
		vd1 = frame->reconnected->data( vh1 );
		vd2 = frame->reconnected->data( vh2 );
		vd3 = frame->reconnected->data( vh3 );
		vd4 = frame->reconnected->data( vh4 );
		vd1.set_weight( 3.0/8.0 );
		vd2.set_weight( 3.0/8.0 );
		vd3.set_weight( 1.0/8.0 );
		vd4.set_weight( 1.0/8.0 );
		frame->reconnected->data( vh1 ) = vd1;
		frame->reconnected->data( vh2 ) = vd2;
		frame->reconnected->data( vh3 ) = vd3;
		frame->reconnected->data( vh4 ) = vd4;

		//and create importance mesh
		this->loadImportance( frame, fh1, im );
		this->loadImportance( frame, fh2, im );
	}
	else if( vd.getType() == VERTEX_VERTEX )
	{
		VData vdata;
		FData fdata;
		//get the coarse vertex
		VHandle vcoarse = frame->coarse->vertex_handle( vd.get_index() );

		//get valence of vertex and set weight values
		//calculate beta
		int n = frame->coarse->valence( vcoarse );
		double inner = pow(((3.0/8.0) + 0.25*(cos((2*PI)/n))),2);
		double beta = (1.0/n) * ((5.0/8.0) - inner);
		double v_weight = 1.0 - (n * beta);
		double weight = beta;

		//now set the weight for the vi
		vd.set_weight( v_weight );
		frame->reconnected->data( vh ) = vd;

		//go through all coarse adjacent vertices and set corresponding
		//connected vertices
		VHandle vConn;
		VVIter vvit( frame->coarse->vv_iter( vcoarse ) );
		for( ; vvit != false; ++vvit )
		{
			vdata = frame->coarse->data( vvit.handle() );
			vConn = frame->reconnected->vertex_handle( vdata.get_index() );
			vdata = frame->reconnected->data( vConn );
			vdata.set_weight( weight );
			frame->reconnected->data( vConn ) = vdata;
		}

		//add the faces to the important mesh
		VFIter vfit( frame->coarse->vf_iter( vcoarse ));
		for( ; vfit != false; ++vfit )
			this->loadImportance( frame, vfit.handle(), im );
	}
}


/**
 *	
 */
void LoopSubdivider::showWeights( const Selection* s )
{

	ModelFrame* frame = s->selectionSource;
	if( s && frame && s->type == VERTEX )
		calcWeights( s );
}



void LoopSubdivider::calcWeights( const Selection* s )
{
	int index = 0;
	EData ed;
	VHandle vh( s->selectedVertex );
	ModelFrame* frame = s->selectionSource;
	PolyMesh* im = new PolyMesh();

	//make sure there is a subdivided frame and make sure the selected face is in the coarse mesh
	if(!( frame->coarse && frame->reconnected && frame->subdivided ))
		return;
	if( !frame->reconnected->is_valid_handle( vh ) )
		return;

	if( frame->getImportanceMesh() )
	{
		frame->deleteImportance();
	}

	clearWeights( frame->reconnected );

	//load the importance mesh with values
	loadImportance( frame, vh, im );

	im->request_face_normals();
	im->request_vertex_normals();
	im->update_normals();
	frame->setImportanceMesh(im);
}


//==============================================================================
// Helper Methods (private)
//==============================================================================

/**
 *	This function calculates the position of a vertex by checking if it's
 *  surrounding edges are sharp.  If it has two sharp edges attached, it's position
 *  will become the coarse vertex with the even bspline mask applied.
 *  If it has three or more sharp edges attached, it will be set to the corresponding 
 *  coarse position.
 */
void LoopSubdivider::doSharpVertex( ModelFrame* f, VHandle& vh )
{
	Point p;
	VData vd;
	int index = 0;
	Sharpness vSharp = isSharp( f->coarse, vh );
	vd = f->coarse->data( vh );

	if( vSharp == CREASE )
	{
		//If this vertex has two sharp edges attached to it, apply the even
		//BSpline mask to it.
		p = doVertexBoundary( f->coarse, vh );
		f->subdivided->set_point( f->subdivided->vertex_handle(vd.get_index()), p );
		f->reconnected->set_color( f->reconnected->vertex_handle(vd.get_index()), sharpEdgeColor );
	}
	else if( vSharp == ONE_EDGE_BOUNDARY )
	{
		//If this vertex has two sharp edges attached to it, apply the even
		//BSpline mask to it.
		p = doVertexBoundary( f->coarse, vh, true );
		f->subdivided->set_point( f->subdivided->vertex_handle(vd.get_index()), p );
		f->reconnected->set_color( f->reconnected->vertex_handle(vd.get_index()), sharpEdgeColor );

	}
	else if( vSharp == CORNER )
	{
		//If there are more than two sharp edges attached to this vertex, make sure no
		//subdivision is applied to it (set it as coarse vertex)
		p = f->coarse->point( vh );
		f->subdivided->set_point( f->subdivided->vertex_handle(vd.get_index()), p );
		f->reconnected->set_color( f->reconnected->vertex_handle(vd.get_index()), sharpEdgeColor );
	}
	else if( vSharp == ONE_EDGE )
	{
		//If this vertex has two sharp edges attached to it, apply the even subdivision mask to it
		p = doSubdivisionMask( f->coarse, vh );
		f->subdivided->set_point( f->subdivided->vertex_handle(vd.get_index()), p );
		f->reconnected->set_color( f->reconnected->vertex_handle(vd.get_index()), sharpEdgeColor );
	}
	else
		f->reconnected->set_color( f->reconnected->vertex_handle(vd.get_index()), coarseVertexColor );
}


/**
 *  This function checks the importance mesh after the subdivision algorithm has run.
 *  If there was a previous importance mesh, it is recalculated to reflect the new
 *  reconnected/coarse mesh vertex positions.
 */
void LoopSubdivider::checkImportance( ModelFrame* frame )
{
	if( frame->getImportanceMesh() )
	{
		int index = 0;
		FHandle fh;
		FData fd;

		//create new importance mesh
		PolyMesh* im = new PolyMesh();
		PolyMesh* imOld = frame->getImportanceMesh();

		//go through each face in the old importance mesh and update its vertex
		//positions - which will be the new importance mesh
		FIter fit(imOld->faces_begin()), fend(imOld->faces_end());
		for( ; fit != fend; ++fit )
		{
			fd = imOld->data( fit.handle() );
			fh = frame->coarse->face_handle( fd.importance() );
			loadImportance( frame, fh, im );
		}
		//get the normals for the new importance mesh
		im->request_face_normals();
		im->request_vertex_normals();
		im->update_normals();

		//set the new importance mesh in the frame
		frame->setImportanceMesh(im);
	}
}


/**
 *	This function loads the "connMesh" with the connectivity mesh based on the internal initialized
 *  "mesh".  The "subMesh" also is loaded with the calculated subdivided mesh.
 */
void LoopSubdivider::createMeshes( )
{
	Point p1;
	FIter fit;
	VIter vit;
	EIter eit;
	FHIter fhit;
	VData vd;
	VHandle vertex;

	// Compute positions for edge vertices and add to subdivided mesh and connected mesh 
	for( eit= mesh->edges_begin(); eit != mesh->edges_end(); ++eit)
		computeEdgeMidpoints( eit.handle() );

	// Compute vertex-vertices and add to connected mesh and subdivided mesh 
	for( vit = mesh->vertices_begin(); vit != mesh->vertices_end(); ++vit )
	{
		//add v_v in connected mesh...and color it green
		vertex = connMesh->add_vertex( mesh->point( vit.handle() ) );

		//save type of vertex in the connected mesh, used later in weight selection
		vd = connMesh->data( vertex );
		vd.setType( VERTEX_VERTEX );
		vd.set_index( vit.handle().idx() );
		connMesh->data( vertex ) = vd;

		connMesh->set_color( vertex, coarseVertexColor );
		mesh->property( connV_V, vit.handle() ) = vertex;
		
		//make sure coarse mesh v_v is colored, too
		mesh->set_color( vit.handle(), coarseVertexColor );

		//add v_v in subdivided mesh
		vertex = subMesh->add_vertex( mesh->point( vit.handle() ) );

		mesh->property( subV_V, vit.handle() ) = vertex;

		// save the index in the coarse mesh of the subdivided v_v (need for sharp edge stuff)
		vd = mesh->data( vit.handle() );
		vd.set_index( vertex.idx() );
		mesh->data(  vit.handle() ) = vd;

		// calculate weighted v_v in subdivided mesh
		calculateV_V(  vit.handle() );

		// save weighted v_v point...will need it to undo sharp edge
		vd = subMesh->data( vertex );
		vd.setPoint( subMesh->point( vertex ) );
		subMesh->data( vertex ) = vd;
	}

	// for each face, will create new faces in connected mesh and subdivided mesh
	// based on e_v_left -> v_v -> e_v_right  and one face based on all edge vertices
	for( fit = mesh->faces_begin(); fit != mesh->faces_end(); ++fit )
	{
		mesh->data(fit.handle()).set_indices(	addFaces( fit ) );

		// make sure the edges that were sharp in the coarse have been subdivided into
		// sharp edges, too.
		setSharpnessInSubdivided( fit.handle() );
	}
}


/*
 *	This function computes the edgepoint for the connected and subdivided mesh for
 *  the specified edge handle.
 *  For connectivity mesh -> the edgepoint is just the midpoint of adjoining vertices
 *  For the subdivided mesh -> the edgepoint is equal to:
 *
 *  3/8*v_adjoinging1 + 3/8*v_adjoinging2 + 1/8*left_opp_v + 1/8*right_opp_v 
 *
 *  If the edge specified is a boundary edge, then the subdivided edge point is
 *  calculated using the cubic bspline odd mask (midpoint of edge vertices).
 */
void LoopSubdivider::computeEdgeMidpoints( EHandle& ehandle )
{
	HHandle heh( mesh->halfedge_handle( ehandle, 0) );
	HHandle opp_heh( mesh->halfedge_handle( ehandle, 1) );
	VHandle va_h, vb_h, vc_h, vd_h;
	VData vd;

	// calculate midpoint of edge - for connected mesh 
	Point  pos1(mesh->point(mesh->to_vertex_handle(heh)));
	Point  pos2(mesh->point(mesh->to_vertex_handle(opp_heh)));
	Point  midpoint( (pos1 + pos2) * 0.5 );
	Point  pNew;
	
	EData ed = mesh->data( ehandle );

	// Now calculate the subdivided mesh edge vertex...first check if it's a boundary point!

	// if the edge is a boundary, the subdivided edge point will just be the midpoint calculated above
	if( mesh->is_boundary( ehandle ) || ed.sharpness() )
		pNew = midpoint;
	else
	{
		//initialize and calculate the new midpoint
		va_h = mesh->to_vertex_handle( heh );
		vb_h = mesh->to_vertex_handle( opp_heh );
		vc_h = mesh->to_vertex_handle( mesh->next_halfedge_handle( heh ) );
		vd_h = mesh->to_vertex_handle( mesh->next_halfedge_handle( opp_heh ) );

		// calculate the weighted edge point 
		PolyMesh::Scalar s = (3.0/8.0);
		PolyMesh::Scalar s2 = (1.0/8.0);
		pNew = s*mesh->point(va_h) + s*mesh->point(vb_h) + s2*mesh->point(vc_h) + s2*mesh->point(vd_h);
	}
		//add the midpoint as the connected mesh edgepoint
	VHandle vertex = connMesh->add_vertex( midpoint );
	connMesh->set_color( vertex, connectedVertexColor );

	//save type of vertex in the connected mesh, used later in weight selection
	vd = connMesh->data( vertex );
	vd.setType( EDGE_VERTEX );
	vd.set_importance( ehandle.idx() );
	connMesh->data( vertex ) = vd;

	/* add midpoint to connected mesh and save handle */
	mesh->property( connE_V, ehandle ) = vertex;

	/* add weighted edge point to subdivided mesh and save handle and its sharpness */
	VHandle vh = subMesh->add_vertex( pNew );

	// save this weighted point (need it later for sharp undoing 
	vd = subMesh->data( vh );
	vd.setPoint( pNew );
	subMesh->data( vh ) = vd;

	ed.set_importance( vh.idx() );
	mesh->data( ehandle ) = ed;
	mesh->property( subE_V, ehandle ) = vh;
}


/*
 *	This method takes in a handle to a coarse mesh face and creates four new faces
 *  based on this face in the connected mesh and subdivided mesh
 *
 *  Three faces are created by joining:  left_edge_vertex->vertex_vertex ->  right->edge_vertex
 *  and one face is created by joining all of the edge vertices of the face           
 */
std::vector<int> LoopSubdivider::addFaces( FIter& fit  )
{
	HHandle fhit( mesh->halfedge_handle( fit.handle()) );
	FHandle fh;
	VHandle	v_vHandlesC[3], e_vHandlesC[3],
							v_vHandlesS[3], e_vHandlesS[3];

	// get all edge vertex handles and vertex-vertex handles in this face
	for( int i(0); i<3; i++ )
	{
		v_vHandlesC[i] = mesh->property( connV_V, mesh->from_vertex_handle( fhit ) );
		e_vHandlesC[i] = mesh->property( connE_V, mesh->edge_handle( fhit ) );
		v_vHandlesS[i] = mesh->property( subV_V, mesh->from_vertex_handle( fhit ) );
		e_vHandlesS[i] = mesh->property( subE_V, mesh->edge_handle( fhit ) );

		fhit = mesh->next_halfedge_handle( fhit );
	}	
	
	// add new faces - this OpenMesh internal function creates the edges/halfedges for us
	std::vector<int> indices;
	EData ed;
	// 4
	indices.push_back((connMesh->add_face( e_vHandlesC[0], e_vHandlesC[1], e_vHandlesC[2] )).idx());
	fh = subMesh->add_face( e_vHandlesS[0], e_vHandlesS[1], e_vHandlesS[2] );
	FEIter feIt( connMesh->fe_iter(fh) );
	
	for(; feIt != false; ++feIt )
	{
		ed = connMesh->data( feIt.handle() );
		ed.setColor( this->connectedVertexColor );
		connMesh->data( feIt.handle() ) = ed;
	}
	// 3
	indices.push_back((connMesh->add_face( v_vHandlesC[2], e_vHandlesC[2], e_vHandlesC[1] )).idx());
	fh = subMesh->add_face( v_vHandlesS[2], e_vHandlesS[2], e_vHandlesS[1] );
	feIt = connMesh->fe_iter(fh);
	
	for(; feIt != false; ++feIt )
	{
		ed = connMesh->data( feIt.handle() );
		ed.setColor( this->connectedVertexColor );
		connMesh->data( feIt.handle() ) = ed;
	}
	// 2
	indices.push_back((connMesh->add_face( v_vHandlesC[1], e_vHandlesC[1], e_vHandlesC[0] )).idx());
	fh = subMesh->add_face( v_vHandlesS[1], e_vHandlesS[1], e_vHandlesS[0] );
	feIt = connMesh->fe_iter(fh);
	
	for(; feIt != false; ++feIt )
	{
		ed = connMesh->data( feIt.handle() );
		ed.setColor( this->connectedVertexColor );
		connMesh->data( feIt.handle() ) = ed;
	}
	// 1
	indices.push_back((connMesh->add_face( v_vHandlesC[0], e_vHandlesC[0], e_vHandlesC[2] )).idx());
	fh= subMesh->add_face( v_vHandlesS[0], e_vHandlesS[0], e_vHandlesS[2] );
	feIt = connMesh->fe_iter(fh);
	for(; feIt != false; ++feIt )
	{
		ed = connMesh->data( feIt.handle() );
		ed.setColor( this->connectedVertexColor );
		connMesh->data( feIt.handle() ) = ed;
	}
	return indices;
}


/**
 *	This function calculates a vertex-vertex in a subdivided mesh given a vertex
 *  handle from the coarse mesh.
 *
 *  The calculation:  
 * 
 *  v_v = (1/n) * ( 5/8 - [ 3/8 + (1/4)*cos(2PI/n) ]^2 )
 * 
 *  If the vertex is a boundary vertex, the cubic bspline even mask is applied
 *  to create the new point:
 *
 *  The calculation:  v_v = (1/8)*v_adjacent_boundary1 + (3/4)*v_coarse + (1/8)*v_adjacent_boundary2
 */
void LoopSubdivider::calculateV_V( VHandle& vh )
{
	Point pNew;

	//check if vertex is sharp (has 2 or more sharp edges attached)
	Sharpness vSharp = isSharp( mesh, vh );

	if( !mesh->is_boundary( vh )  && vSharp==NO_SHARPNESS )
	{
		pNew = doSubdivisionMask( mesh, vh );
	}
	else if( vSharp == ONE_EDGE_BOUNDARY )
		pNew = doVertexBoundary( mesh, vh, true );

	else if( mesh->is_boundary( vh ) || vSharp == CREASE )/* vertex is a boundary or a crease-vertex */
		pNew = doVertexBoundary( mesh, vh );

	else //is a corner point, so do nothing to it
		pNew = mesh->point( vh );
	
	if( vSharp != NO_SHARPNESS )
		connMesh->set_color( mesh->property( connV_V, vh ), sharpEdgeColor );

	// set the point in the subdivided mesh to be this new calculated point 
	subMesh->set_point(  mesh->property( subV_V, vh ), pNew );
}



/**
 *	This function sets the subdivided edges corresponding to the supplied coarse edge
 *  as sharp or not.  The new positions of its corresponding vertices are calculated
 *  and applied, as well.
 */
void LoopSubdivider::doSharpEdgeSetting( ModelFrame* f, EHandle& eh )
{
	EData ed, le, re;
	VHandle vh1, vh2, vhSub1, vhSub2;
	VData vd1, vd2, vdEdge;
	HHandle heh;
	bool sharpVal;

	ed = f->coarse->data( eh );
	sharpVal = ed.sharpness() ? false : true; 
	ed.set_sharp( sharpVal );
	f->coarse->data( eh ) = ed;

	heh = f->coarse->halfedge_handle(eh,0);
	vh1 = f->coarse->from_vertex_handle(heh);
	vh2 = f->coarse->to_vertex_handle(heh);
	
	// get the edge vertex data corresponding to this edge
	vdEdge = f->subdivided->data( f->subdivided->vertex_handle(ed.importance()) );

	if( sharpVal )
	{
		//find midpoint of two edge vertices...save this as the position of corresponding
		//edge vertex in the subdivided mesh
		Point  pos1(f->coarse->point(vh1));
		Point  pos2(f->coarse->point(vh2));
		Point  midpoint( (pos1 + pos2) * 0.5 );
		f->subdivided->set_point(  f->subdivided->vertex_handle(ed.importance()), midpoint );
		f->reconnected->set_color( f->reconnected->vertex_handle(ed.importance()), sharpEdgeColor );
	}
	else
	{
		f->subdivided->set_point( f->subdivided->vertex_handle(ed.importance()), vdEdge.getPoint() );
		f->reconnected->set_color( f->reconnected->vertex_handle(ed.importance()), connectedVertexColor );
	}
	// now set the edges in the subdivided mesh corresponding to this coarse mesh
	// sharp, as well.
	
	EHandle ledge = f->subdivided->edge_handle(vdEdge.left_edge_index());
	EHandle redge = f->subdivided->edge_handle(vdEdge.right_edge_index());

	// save the sharpness in each subdivided edge
	le = f->subdivided->data( ledge );
	le.set_sharp(sharpVal);
	re = f->subdivided->data( redge );
	re.set_sharp(sharpVal);
	f->subdivided->data( ledge ) = le;
	f->subdivided->data( redge ) = re;

	EData led = connMesh->data( connMesh->edge_handle(ledge.idx()) );
	EData red = connMesh->data( connMesh->edge_handle(redge.idx()) );
		
	// if undoing sharp feature, reset vertex-vertices to old points (and reset color)
	if( !sharpVal )
	{
		led.setColor( coarseVertexColor );
		red.setColor( coarseVertexColor );
		vd1 = f->coarse->data( vh1 );
		vd2 = f->coarse->data( vh2 );
		vhSub1 = f->subdivided->vertex_handle(vd1.get_index());
		vhSub2 = f->subdivided->vertex_handle(vd2.get_index());

		f->subdivided->set_point( vhSub1, f->subdivided->data(vhSub1).getPoint() );
		f->subdivided->set_point( vhSub2,  f->subdivided->data(vhSub2).getPoint() );
	}
	else
	{
		led.setColor( sharpEdgeColor );
		red.setColor( sharpEdgeColor );
	}
	connMesh->data( connMesh->edge_handle(ledge.idx()) ) = led;
	connMesh->data( connMesh->edge_handle(redge.idx()) ) = red;

	//recalculate surrounding subdivided points with sharp edge added/remove
	doSharpVertex( f, vh1 );
	doSharpVertex( f, vh2 );
}